package com.wgo.precise.client.ui.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.util.ListenerList;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.eclipse.ui.views.properties.TextPropertyDescriptor;

import com.wgo.precise.client.ui.controller.Assert;
import com.wgo.precise.client.ui.controller.listeners.IModelUpdateListener;
import com.wgo.precise.client.ui.model.facade.IModelPersistable;
import com.wgo.precise.client.ui.model.facade.IPropertiesContainer;
import com.wgo.precise.client.ui.view.util.GenericModelPropertySource;
import com.wgo.precise.client.ui.view.util.ModelItemPropertySource;
import com.wgo.precise.common.VO.Persistent;
import com.wgo.precise.common.VO.PropertyDef;

/**
 * @author petterei
 *
 * @version $Id: PropertyDefWrapper.java,v 1.2 2006-01-31 22:40:30 petterei Exp $
 */
public class PropertyDefWrapper extends AbstractPropertySpesification implements IModelPersistable, IPropertiesContainer  {

	private PropertyDef domainDefinition = null;

	private static List<ClientPropertyDefinition<PropertyDefWrapper>> definitions = new ArrayList<ClientPropertyDefinition<PropertyDefWrapper>>();

    /**
     * The id is the propertyspecification-identifier for the PropertyValue.
     * 
     * This is called current because it only contains the values from 
     * the property-specifications from the current Configuration/CategoryType.
     * Older and obsolete property-values can be stored in another hash-map to 
     * support ie. history of an object.  
     */
    private Map<AbstractPropertySpesification,PropertyValue> currentProperties = new HashMap<AbstractPropertySpesification,PropertyValue>();

    private ListenerList listeners = new ListenerList();
    
	protected PropertyDefWrapper(PropertyDef definition) {
		this.domainDefinition = definition;
		initProperties();
	}
	
	@Override
	public Long getIdentifier() {
		return domainDefinition.getDbId();
	}
	
	@Override
	public String getLabel() {
		return domainDefinition.getName();
	}

	@Override
	public void setLabel(String label) {
		domainDefinition.setName(label);
		notifyChange();
	}

	@Override
	public String getDescription() {
		return domainDefinition.getDescription();
	}

	@Override
	public void setDescription(String description) {
		domainDefinition.setDescription(description);
		notifyChange();
	}

	@Override
	public PropertyType getPropertyType() {
		return PropertyType.valueOf(domainDefinition.getType().name());
	}

	@Override
	public String getDefaultValue() {
		return null;
		//		return domainDefinition.getDefaultValue();
	}
	
	@Override
	public void setDefaultValue(String value) {
//		domainDefinition.setDefaultValue(value);
	}

	@Override
	public int getPosition() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setPosition(int position) { // is position used at all?
		// TODO Auto-generated method stub
	}

	public PropertyDef getDomainModelDefinition() {
		return domainDefinition;
	}

	/**
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	public Object getAdapter(Class adapter) {
		if (adapter == IPropertySource.class) {
	        List<AbstractPropertySpesification> defs = new ArrayList<AbstractPropertySpesification>();
	        defs.addAll(definitions);
			ModelItemPropertySource propertySource = new GenericModelPropertySource(this, defs, "PropertiesDefinition"){
				@Override
				protected PropertyDescriptor[] createPropertyDescriptors(Collection<AbstractPropertySpesification> specs, String category) {
					PropertyDescriptor[] propertyDescriptors = super.createPropertyDescriptors(specs, category);
					PropertyDescriptor oldDescriptor = propertyDescriptors[definitions.indexOf(defaultValue)];
					propertyDescriptors[definitions.indexOf(defaultValue)] = new TextPropertyDescriptor(oldDescriptor.getId(), oldDescriptor.getDisplayName()) {
						@Override
						public CellEditor createPropertyEditor(Composite parent) {
							return getCellEditor(parent);
						}
					};
					propertyDescriptors[definitions.indexOf(defaultValue)].setCategory(oldDescriptor.getCategory());
					propertyDescriptors[definitions.indexOf(defaultValue)].setDescription(oldDescriptor.getDescription());
					return propertyDescriptors;
				}
			};
			return propertySource;
		} 
		return null;
	}
	
	private static PropertySpecification<PropertyDefWrapper> defaultValue;
	static  {
		PropertySpecification<PropertyDefWrapper> label;
		label = new PropertySpecification<PropertyDefWrapper>(Configuration.getInstance().getNextPropertyDefDefinitionId(), "Label", PropertyType.STRING) {
			@Override
			protected Object getValue(PropertyDefWrapper wrapper) {
				return wrapper.getLabel();
			}
			@Override
			protected void setValue(PropertyDefWrapper wrapper, Object value) {
				wrapper.setLabel((String)value);
			}
		};
		label.setDescription("The label");
		label.setPosition(Configuration.getInstance().getNextPropertyDefDefinitionPosition());
		label.setDefaultValue("No label set.");
		definitions.add(label);
		
		PropertySpecification<PropertyDefWrapper> description;
		description = new PropertySpecification<PropertyDefWrapper>(Configuration.getInstance().getNextPropertyDefDefinitionId(), "Description", PropertyType.STRING) {
			@Override
			protected Object getValue(PropertyDefWrapper wrapper) {
				return wrapper.getDescription();
			}
			@Override
			protected void setValue(PropertyDefWrapper wrapper, Object value) {
				wrapper.setDescription((String)value);
			}
		};
		description.setDescription("The description.");
		description.setPosition(Configuration.getInstance().getNextPropertyDefDefinitionPosition());
		description.setDefaultValue("No description set.");
		definitions.add(description);

		PropertySpecification<PropertyDefWrapper> type;
		type = new PropertySpecification<PropertyDefWrapper>(Configuration.getInstance().getNextPropertyDefDefinitionId(), "Property Type", PropertyType.STRING) {
			@Override
			protected Object getValue(PropertyDefWrapper wrapper) {
				PropertyType domainType = wrapper.getPropertyType();
				return domainType.getDescription() + " (" + domainType.name() + ")";
			}
			@Deprecated // The value is read only and should never be used, hence the setValue-method should never be used..
			@Override
			protected void setValue(@SuppressWarnings("unused") PropertyDefWrapper wrapper, @SuppressWarnings("unused") Object value) {}
		};
		type.setReadOnly(true);
		type.setDescription("The type of this PropertyDefinition");
		type.setPosition(Configuration.getInstance().getNextPropertyDefDefinitionPosition());
		type.setDefaultValue("");
		definitions.add(type);

		defaultValue = new PropertySpecification<PropertyDefWrapper>(Configuration.getInstance().getNextPropertyDefDefinitionId(), "Default value", PropertyType.STRING) {
			@Override
			protected Object getValue(PropertyDefWrapper wrapper) {
				return wrapper.getDefaultValue();
			}
			@Override
			protected void setValue(PropertyDefWrapper wrapper, Object value) {
				wrapper.setDefaultValue((String)value);
			}
		};
		defaultValue.setDescription("The description.");
		defaultValue.setPosition(Configuration.getInstance().getNextPropertyDefDefinitionPosition());
		defaultValue.setDefaultValue("No description set.");
		definitions.add(defaultValue);
	}

    private void initProperties() {
    	for (ClientPropertyDefinition<PropertyDefWrapper> definition : definitions) {
           setPropertyValue(new ClientPropertyValue<PropertyDefWrapper>(this, definition));
    	}
    }
    /**
     * Only internally. Se variable description for further information.
     */
    protected void setPropertyValue(PropertyValue propertyValue) {
        currentProperties.put(propertyValue.getPropertySpesification(), propertyValue);
    }
    
	public PropertyValue getCurrentPropertyValue(AbstractPropertySpesification spec) {
        return currentProperties.get(spec);
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#getCurrentPropertyValues()
	 */
	public List<PropertyValue> getCurrentPropertyValues() {
        List<PropertyValue> listProperties = new ArrayList<PropertyValue>();
        for (PropertyValue value : currentProperties.values()) {
            listProperties.add(value);
        }
        return  listProperties;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#isPropertySet(com.wgo.precise.client.ui.model.AbstractPropertySpesification)
	 */
	public boolean isPropertySet(AbstractPropertySpesification definition) {
    	return currentProperties.containsKey(definition);
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#setPropertyDefaultValue(com.wgo.precise.client.ui.model.AbstractPropertySpesification)
	 */
	public void setPropertyDefaultValue(AbstractPropertySpesification spec) {
        Assert.notNull(spec);
        PropertyValue propertyValue = currentProperties.get(spec);
        if (propertyValue == null) {
//  TODO?          currentProperties.put(spec , new PropertyValue(null , spec));
        } else {
            propertyValue.setDefaultValue();
        }
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#setPropertyValueFromCellEditor(com.wgo.precise.client.ui.model.AbstractPropertySpesification, java.lang.Object)
	 */
	public boolean setPropertyValueFromCellEditor(AbstractPropertySpesification spec, Object value) {
        Assert.notNull(spec);
        PropertyValue propertyValue = currentProperties.get(spec);

        boolean internalDirty = false;
        
        if (propertyValue == null) {
//       TODO     propertyValue = new PropertyValue(value , spec);
            setPropertyValue(propertyValue); //??
            internalDirty = true;
        } else if (value != null) {
            boolean valueChanged = propertyValue.setValueFromCellEditor(value);
            internalDirty = valueChanged;
        }

        return internalDirty;
	}
	
	public void addChangeListener(IModelUpdateListener listener){
		listeners.add(listener);
	}

	public void removeChangeListener(IModelUpdateListener listener){
		listeners.remove(listener);
	}

	private void notifyChange(){
		for (Object listener : listeners.getListeners()) {
			((IModelUpdateListener)listener).onModelUpdate(null);//TODO
		}
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getDomainModelObject()
	 */
	public Persistent getDomainModelObject() {
		return domainDefinition;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getDomainObjectType()
	 */
	@SuppressWarnings("unchecked")
	public Class<? extends Persistent> getDomainObjectType() {
		return null != getDomainModelObject() ? (Class<? extends Persistent>) getDomainModelObject().getClass().getInterfaces()[0] : null;
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getImage()
	 */
	public Image getImage() {
		return null; //TODO: add image
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getSimpleName()
	 */
	public String getSimpleName() {
		return getLabel();
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getText()
	 */
	public String getText() {
		return getSimpleName() + "[" + getPropertyType().name() + "]";
	}

	/**
	 * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getToolTipText()
	 */
	public String getToolTipText() {
		return getDescription();
	}

}
